//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#ifndef __DEBUG_STUB_H__
#define __DEBUG_STUB_H__

#include <ddk.h>
#include <mantle.h>
#include <core.h>
#include <_hal.h>

class DebuggerStub : public CDeviceDriver
{
friend Boolean TryToSendReceiveDebugMsg(
        /* [in] */ UInt32 reason,
        /* [in] */ UInt32 handle,
        /* [in] */ CProcess * targetProcess,
        /* [in] */ Thread * targetThread);

public:
    CARAPI Read(
        /* [in] */ Int64 u64Offset,
        /* [in] */ Int32 bytesToRead,
        /* [out] */ MemoryBuf * pBuffer,
        /* [out] */ IEvent * * ppCompletionEvent);

    CARAPI Write(
        /* [in] */ Int64 u64Offset,
        /* [in] */ const MemoryBuf & buffer,
        /* [out] */ Int32 * pBytesWritten,
        /* [out] */ IEvent * * ppCompletionEvent);

    CARAPI Control(
        /* [in] */ Handle32 nControlCode,
        /* [in] */ const MemoryBuf & inBuffer,
        /* [out] */ MemoryBuf * pOutBuffer,
        /* [out] */ IEvent * * ppCompletionEvent);

    virtual void Dispose()
    {
        assert( m_nTargetProcessId == 0);
        delete this;
    }

    DebuggerStub() :
        m_WaitEvent(TRUE), m_pTargetProcess(NULL),
        m_nTargetProcessId(0), m_nTriggerThreadId(0), m_nDebuggerThreadId(0),
        m_bHaveStopped(FALSE)
    {
    }

private:
    void SendDebugEvent(
        CProcess * pTargetProcess,
        Thread * pTargetThread,
        EventState state,
        UInt32 handle);

    Boolean SuspendAllTargetThreads(CProcess * pTargetProcess);

    void ResumeAllTargetThreads(CProcess * pTargetProcess);

    void FillDebugEvent(EventState state, UInt32 handle);

    CProcess * GetProcessById(ProcessId pid);

    Thread * GetThreadFromProcess(CProcess * pProcess, ThreadId tid);

    Thread * GetThreadFromTargetProcess(ThreadId tid);

    ECode CheckIntegrality();

    ECode CheckIntegralityAndDisablePreemption(PBoolean);

    ECode CheckIntegralityAndLockMemoryAllocator(PBoolean);

    ECode WaitForDebugEvent(
        /* [out] */ LPDEBUG_EVENT pDebugEvent,      // debug event information
        /* [in] */ UInt32 dwMilliseconds);           // time-out value

    ECode ContinueDebugEvent(
        /* [in] */ UInt32 dwThreadId,                // thread to continue
        /* [in] */ UInt32 dwContinueStatus);         // continuation status

    ECode AttachActiveProcess(
        /* [in] */ UInt32 dwProcessId);              // process to be debugged

    ECode DetachActiveProcess();

    ECode GetThreadContext(
        /* [in] */ UInt32 dwThreadID,        // handle to thread with context
        /* [out] */ Context *pContext);     // context structure

    ECode SetThreadContext(
        /* [in] */ UInt32 dwThreadID,        // handle to thread with context
        /* [in] */ Context *pContext);      // context structure

    ECode ReadProcessMemory(
        /* [in] */ PVoid pBaseAddress,              // base of memory area
        /* [in] */ PVoid pBuffer,                   // data buffer
        /* [in] */ size_t nSize,                    // number of bytes to read
        /* [out] */ size_t * pNumberOfBytesRead);   // number of bytes read

    ECode WriteProcessMemory(
        /* [in] */ PVoid pBaseAddress,              // base of memory area
        /* [in] */ PVoid pBuffer,                   // data buffer
        /* [in] */ size_t nSize,                    // count of bytes to write
        /* [out] */ size_t * pNumberOfBytesWritten);// count of bytes written

    ECode SetBreakPoint(
        /* [in] */ UInt32 dwThreadID,
        /* [out] */ PVoid pOrigInstruction);

    ECode ExchangeProcessMemory(
        /* [in] */ PVoid pBaseAddress,  // base of memory area
        /* [in] */ PVoid pBuffer,       // data buffer
        /* [in] */ size_t nSize,        // count of bytes to be written
        /* [out] */ PVoid pOutBuffer);  // buffer that receives data

    ECode GetThreadStackBase(
        /* [in] */ UInt32 dwThreadId,
        /* [out] */ PVoid * pStackBase);

/*
 * used for pausing debuggee process after continued in debug mode.
 * this is different from 'attach', since attach happens before debuggee
 * enters debug mode.
 *
 * this function was modified from CKernel::CreateThread() in
 * zener/kernel/major/earth/Ckernel.cpp
 */
    ECode SuspendProcess(
        /* [in] */ ProcessId dwProcessId, // debuggee process ID
        /* [in] */ Address entry, // address of ThreadEntry. 
                //this is the main entrance of any new thread

        /* [in] */ Address UserEntry, // address of DebugBreakThread. 
                //we use this function to breakin debuggee process

        /* [in] */ PVoid nUserArg, // since DebugBreakThread use no param for now, 
                //this param useless

        /* [out] */ ThreadId *pdwThreadId); // Thread ID returns to debugger, so the 
                //debugger can add DebugBreakThread to its thread list by itself 
                //if CREATE_THREAD event cannot be recved.

    ThreadId INLINE GetThreadId(Thread *pThd = NULL)
    {
        ThreadId id;
        if (pThd) {
            verify(NOERROR == pThd->GetId(&id));
        }
        else {
            ::GetCurrentThread()->GetId(&id);
        }

        return id;
    }

    ProcessId INLINE GetProcessId(CProcess *pProc = NULL)
    {
        ProcessId id;

        if (pProc) {
            verify(NOERROR == pProc->GetId(&id));
        }
        else {
            ::GetCurrentProcess()->GetId(&id);
        }

        return id;
    }

    Boolean CheckMemoryBlock(
        /* [in] */ UInt32 base,
        /* [in] */ UInt32 size,
        /* [out] */ VSegment ** ppSegment);

protected:
    DzEvent     m_ContinueEvent;
    DzEvent     m_WaitEvent;
    DEBUG_EVENT m_DebugEvent;

    CProcess   *m_pTargetProcess;
    ProcessId   m_nTargetProcessId;
    ThreadId    m_nTriggerThreadId;
    ThreadId    m_nDebuggerThreadId;
    Boolean        m_bHaveStopped;
    char        m_RWBuf[DBGSTUB_BUFFER_SIZE];
};

#endif // __DEBUG_STUB_H__
